﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using ArgusLib.Collections;

namespace ArgusLib.Fractals.OrbitFractals
{
	public abstract class DataSet<T> : IDataSet
	{
		public int DimensionX { get; private set; }
		public int DimensionY { get; private set; }
		public Parameter CalculationParameter { get; private set; }
		Fractals.IParameter Fractals.IDataSet.CalculationParameter { get { return this.CalculationParameter; } }

		//private object _lock = new object();

		public ReadOnlyArray2<int> Iterations { get; private set; }

		Wrapper[,] zValues;
		int[,] iterations;

		public DataSet(Parameter parameter)
		{
			this.DimensionX = parameter.PixelWidth;
			this.DimensionY = parameter.PixelHeight;
			this.CalculationParameter = parameter;
			this.Reset();
		}

		public void Reset()
		{
			this.zValues = new Wrapper[this.DimensionX, this.DimensionY];
			this.iterations = new int[this.DimensionX, this.DimensionY];
			//this.iterations.Initialize(0);
			this.Iterations = new ReadOnlyArray2<int>(this.iterations);
		}

		public abstract T Convert(int x, int y);

		object IDataSet.Convert(int x, int y) { return this.Convert(x, y); }

		public T this[int x, int y]
		{
			get { return this.zValues[x, y].Value; }
			set
			{
				if (this.zValues[x, y] == null)
				{
					this.zValues[x, y] = new Wrapper(value);
				}
				else
				{
					T oldValue = this.zValues[x, y].Value;
					this.zValues[x, y].Value = value;
					this.OnZValueChanged(x, y, oldValue, value);
				}
			}
		}

		object IDataSet.this[int x, int y]
		{
			get
			{
				if (this.zValues[x, y] == null)
					return null;
				else
					return this.zValues[x, y].Value;
			}
			set
			{
				//if (value is T)
					this[x, y] = (T)value;
			}
		}

		public virtual void PostProcess() { }

		public bool IsNull(int x, int y)
		{
			//lock (this._lock)
			//{
				return (this.zValues[x, y] == null);
			//}
		}

		protected virtual void OnZValueChanged(int x, int y, T oldValue, T newValue)
		{
			this.iterations[x, y]++;
		}

		private class Wrapper
		{
			public T Value;

			public Wrapper(T value)
			{
				this.Value = value;
			}
		}
	}
}
